/*
 * Copyright (c) 2024 OpenHiTLS. All rights reserved.
 * Licensed under the OpenHiTLS license.
 */
package org.openhitls.tls;

import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocket;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.ServerSocket;

/**
 * SSLServerSocket implementation backed by openHiTLS.
 *
 * <p>This class creates server sockets that accept TLS connections.</p>
 */
public class HiTlsSSLServerSocket extends SSLServerSocket {

    private final HiTlsSSLContextSpi context;
    private final ServerSocket serverSocket;
    private String[] enabledCipherSuites;
    private String[] enabledProtocols;
    private boolean needClientAuth = false;
    private boolean wantClientAuth = false;
    private boolean useClientMode = false;
    private boolean enableSessionCreation = true;

    HiTlsSSLServerSocket(HiTlsSSLContextSpi context, int port) throws IOException {
        this.context = context;
        this.serverSocket = new ServerSocket(port);
        initDefaults();
    }

    HiTlsSSLServerSocket(HiTlsSSLContextSpi context, int port, int backlog) throws IOException {
        this.context = context;
        this.serverSocket = new ServerSocket(port, backlog);
        initDefaults();
    }

    HiTlsSSLServerSocket(HiTlsSSLContextSpi context, int port, int backlog, InetAddress bindAddr)
            throws IOException {
        this.context = context;
        this.serverSocket = new ServerSocket(port, backlog, bindAddr);
        initDefaults();
    }

    private void initDefaults() {
        this.enabledCipherSuites = context.getEnabledCipherSuites();
        this.enabledProtocols = context.getEnabledProtocols();
    }

    @Override
    public Socket accept() throws IOException {
        Socket plainSocket = serverSocket.accept();
        HiTlsSSLSocket sslSocket = new HiTlsSSLSocket(context, plainSocket,
            plainSocket.getInetAddress().getHostName(), plainSocket.getPort(), true);

        // Configure the SSL socket
        sslSocket.setUseClientMode(useClientMode);
        sslSocket.setNeedClientAuth(needClientAuth);
        sslSocket.setWantClientAuth(wantClientAuth);
        sslSocket.setEnabledCipherSuites(enabledCipherSuites);
        sslSocket.setEnabledProtocols(enabledProtocols);
        sslSocket.setEnableSessionCreation(enableSessionCreation);

        return sslSocket;
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return context.getSupportedCipherSuites();
    }

    @Override
    public String[] getEnabledCipherSuites() {
        return enabledCipherSuites.clone();
    }

    @Override
    public void setEnabledCipherSuites(String[] suites) {
        this.enabledCipherSuites = suites.clone();
    }

    @Override
    public String[] getSupportedProtocols() {
        return context.getSupportedProtocols();
    }

    @Override
    public String[] getEnabledProtocols() {
        return enabledProtocols.clone();
    }

    @Override
    public void setEnabledProtocols(String[] protocols) {
        this.enabledProtocols = protocols.clone();
    }

    @Override
    public void setNeedClientAuth(boolean need) {
        this.needClientAuth = need;
    }

    @Override
    public boolean getNeedClientAuth() {
        return needClientAuth;
    }

    @Override
    public void setWantClientAuth(boolean want) {
        this.wantClientAuth = want;
    }

    @Override
    public boolean getWantClientAuth() {
        return wantClientAuth;
    }

    @Override
    public void setUseClientMode(boolean mode) {
        this.useClientMode = mode;
    }

    @Override
    public boolean getUseClientMode() {
        return useClientMode;
    }

    @Override
    public void setEnableSessionCreation(boolean flag) {
        this.enableSessionCreation = flag;
    }

    @Override
    public boolean getEnableSessionCreation() {
        return enableSessionCreation;
    }

    @Override
    public SSLParameters getSSLParameters() {
        SSLParameters params = new SSLParameters();
        params.setCipherSuites(enabledCipherSuites);
        params.setProtocols(enabledProtocols);
        params.setNeedClientAuth(needClientAuth);
        params.setWantClientAuth(wantClientAuth);
        return params;
    }

    @Override
    public void setSSLParameters(SSLParameters params) {
        if (params.getCipherSuites() != null) {
            setEnabledCipherSuites(params.getCipherSuites());
        }
        if (params.getProtocols() != null) {
            setEnabledProtocols(params.getProtocols());
        }
        setNeedClientAuth(params.getNeedClientAuth());
        setWantClientAuth(params.getWantClientAuth());
    }

    @Override
    public void close() throws IOException {
        serverSocket.close();
    }

    @Override
    public boolean isClosed() {
        return serverSocket.isClosed();
    }

    @Override
    public InetAddress getInetAddress() {
        return serverSocket.getInetAddress();
    }

    @Override
    public int getLocalPort() {
        return serverSocket.getLocalPort();
    }
}
